package com.ht.api.controller;

import java.util.Arrays;
import java.util.List;

import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.ht.api.bean.AjaxResult;
import com.ht.api.bean.UserInfo;
import com.ht.api.call.ICall;
import com.ht.api.db.Db;
import com.ht.api.db.DbList;
import com.ht.api.db.Entity;
import com.ht.api.db.SqlParser;
import com.ht.api.db.cache.Cache;
import com.ht.api.db.exception.HtException;
import com.ht.api.db.metadata.Metadata;
import com.ht.api.util.UserUtil;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import jakarta.annotation.Resource;

/**
 * 元数据管理接口
 * @author asq
 * @createTime 2025年1月2日 12:27:19
 */
@RestController
@RequestMapping("/meta")
public class MetaController {
	/** 不允许操作的系统表名 */
	private static final List<String> SYS_TABLES = Arrays.asList("db_table", "db_column");
	/** 不允许操作的系统字段名 */
	private static final List<String> SYS_COLUMNS = Arrays.asList("main_id", "sys_time", "sys_deleted", "sys_creator");
	
	/** 新增 */
	private static final String ADD = "add";
	/** 删除 */
	private static final String DEL = "del";
	/** 修改 */
	private static final String EDIT = "edit";
	/** 复制 */
	private static final String COPY = "copy";
	/** 设置主键 */
	private static final String PRIMARY = "primary";
	
	@Resource(name = "db_table_cache")
	private Cache dbTableCache;
	
	// 表操作
	@PostMapping("/table/{opt}")
	public AjaxResult tableOpt(@PathVariable String opt, @RequestBody Entity entity) {
		AjaxResult result = AjaxResult.success();
		
		// 验证表结构操作权限
		checkPerms("table", opt);
		
		// 去除一些字段的空格
		trim(entity);
		
		// 根据操作类型的不同，作具体操作
		String tableCode = entity.getStr("code");
		switch (opt) {
		case ADD:
			checkField(entity, "code", "name", "alias", "tenant"); // 检查是否包含这些字段
			checkSystemTable(tableCode); // 防止操作系统表
			checkTableCode(entity, false); // 检查表名是否重复
			Metadata.addTable(entity);
			result.put(ICall.MAIN_ID, entity.getStr(ICall.MAIN_ID)); // 返回主键给前端
			break;
			
		case DEL:
			checkField(entity, "code"); // 检查是否包含这些字段
			checkSystemTable(tableCode); // 防止操作系统表
			Metadata.delTable(Entity.create(tableCode).set("code", tableCode));
			break;
			
		case EDIT:
			checkField(entity, "main_id"); // 检查是否包含这些字段
			checkSystemTable(tableCode); // 防止操作系统表
			checkTableCode(entity, true); // 检查表名是否重复
			Metadata.modifyTable(entity);
			break;
			
		case COPY:
			checkField(entity, "old_code", "code", "alias", "name"); // 检查是否包含这些字段
			String oldTableCode = entity.getStr("old_code");
			checkSystemTable(oldTableCode); // 防止操作系统表（验证旧表名）
			checkSystemTable(tableCode); // 防止操作系统表（验证新表名）
			checkTableCode(entity, false); // 检查表名是否重复
			if (!exists(oldTableCode)) throw new HtException(String.format("表[%s]不存在~", oldTableCode));
			Metadata.copyTable(oldTableCode, tableCode, entity.getStr("alias"), entity.getStr("name"));
			break;
			
		default:
			throw new HtException("不支持的表操作：" + opt);
		}
		
		// 更新表缓存
		dbTableCache.load();
		
		return result;
	}

	// 列操作
	@PostMapping("/column/{opt}")
	public AjaxResult columnOpt(@PathVariable String opt, @RequestParam(required = false) Boolean enabled, 
			@RequestBody Entity entity) {
		AjaxResult result = AjaxResult.success();
		
		// 验证表字段操作权限
		checkPerms("column", opt);
		
		// 去除一些字段的空格
		trim(entity);
		
		// 根据操作类型的不同，作具体操作
		switch (opt) {
		case ADD:
			checkField(entity, "table_code", "code", "name", "type"); // 检查是否包含这些字段
			checkSystemColumn(entity); // 防止操作系统字段
			checkColumnCode(entity, false); // 检查列名是否重复
			Metadata.addColumn(entity);
			result.put(ICall.MAIN_ID, entity.getStr(ICall.MAIN_ID)); // 返回主键给前端
			break;
			
		case DEL:
			checkField(entity, "table_code", "code"); // 检查是否包含这些字段
			checkSystemColumn(entity); // 不允许操作的系统字段
			Metadata.delColumn(entity);
			break;
			
		case EDIT:
			checkField(entity, "main_id", "table_code"); // 检查是否包含这些字段
			checkSystemColumn(entity); // 防止操作系统字段
			checkColumnCode(entity, true); // 检查列名是否重复
			Metadata.modifyColumn(entity);
			break;
			
		case PRIMARY:
			checkField(entity, "main_id"); // 检查是否包含这些字段
			String mainId = entity.getStr("main_id");
			Entity dbEntity = Db.createSqlParser("db_column").findById(mainId);
			if (dbEntity == null) {
				throw new HtException("main_id指定的列不存在:" + mainId);
			}
			if (enabled == null) {
				throw new HtException("请显式指定enabled参数~");
			}
			checkSystemColumn(dbEntity); // 防止操作系统字段
			Metadata.setPrimary(dbEntity, enabled);
			break;
			
		default:
			throw new HtException("不支持的表操作：" + opt);
		}
		
		return result;
	}

	/**
	 * 验证是否具有操作权限
	 * @param target 要操作的目标
	 * @param opt 要操作的类型
	 */
	private void checkPerms(String target, String opt) {
		UserInfo login = UserUtil.getLoginUser();
		if (login.isAdmin()) {
			return ;
		}
		
		// 查询用户的角色列表
		StringBuilder sb = new StringBuilder();
		sb.append("select * from sys_role where main_id in(");
		sb.append("  SELECT role_id FROM `sys_user_role` where user_id=? and sys_deleted=1");
		sb.append(") and sys_deleted=1 and status=0");
		DbList roleList = Db.query(sb.toString(), login.getId());
		
		// 验证所有角色的权限配置，看是否允许当前操作
		String fieldPath = String.format("meta_perm.%s_%s", target, opt);
		for (Entity r : roleList) {
			String configStr = r.getStr("config");
			if (StrUtil.isNotBlank(configStr)) {
				JSONObject config = JSONUtil.parseObj(configStr);
				boolean allow = JSONUtil.getByPath(config, fieldPath, false);
				if (allow) {
					return;
				}
			}
		}
		
		// 如果所有角色都没包含当前操作权限，则直接拒绝
		String targetName = "table".equals(target) ? "表结构" : "表字段";
		throw new HtException(String.format("没有%s的%s权限", targetName, opt));
	}
	
	/**
	 * 验证表名是否存在
	 * @param tableCode
	 */
	private boolean exists(String tableCode) {
		SqlParser sqlParser = Db.createSqlParser("db_table").where("code", tableCode);
		return sqlParser.hasRow();
	}

	/**
	 * 检查表名是否重复
	 * @param entity 
	 * @param isEdit 是否是修改
	 */
	private void checkTableCode(Entity entity, boolean isEdit) {
		String oldCode = null;
		String newCode = entity.getStr("code");
		String mainId = entity.getStr("main_id");
		
		// 如果是修改
		if (isEdit) {
			Entity oldEntity = Db.createSqlParser("db_table").findById(mainId);
			if (oldEntity != null) {
				oldCode = oldEntity.getStr("code");
			} else {
				throw new HtException("main_id指定的表不存在:" + mainId);
			}
		}
		
		// 判断表名是否重复
		SqlParser sqlParser = Db.createSqlParser("db_table").where("code", newCode);
		if (isEdit) {
			sqlParser.where("code", "!=", oldCode);
		}
		boolean hasRow = sqlParser.hasRow();
		if (hasRow) {
			throw new HtException(String.format("表名[%s]已存在~", newCode));
		}
		
		// 判断别名是否重复（修改时不用检查，因为别名不允许修改）
		if (!isEdit) {
			String newAlias = entity.getStr("alias");
			SqlParser sqlParserAlias = Db.createSqlParser("db_table").where("alias", newAlias);
			boolean hasRowAlias = sqlParserAlias.hasRow();
			if (hasRowAlias) {
				throw new HtException(String.format("别名[%s]已存在~", newAlias));
			}
		}
	}

	/**
	 * 检查列名是否重复
	 * @param entity 
	 * @param isEdit 是否是修改
	 */
	private void checkColumnCode(Entity entity, boolean isEdit) {
		String oldCode = null;
		String tableCode = entity.getStr("table_code");
		String newCode = entity.getStr("code");
		String mainId = entity.getStr("main_id");
		
		// 如果是修改
		if (isEdit) {
			Entity oldEntity = Db.createSqlParser("db_column").findById(mainId);
			if (oldEntity != null) {
				oldCode = oldEntity.getStr("code");
			} else {
				throw new HtException("main_id指定的列不存在:" + mainId);
			}
		}
		
		// 判断列名是否重复
		SqlParser sqlParser = Db.createSqlParser("db_column").where("table_code", tableCode).where("code", newCode);
		if (isEdit) {
			sqlParser.where("code", "!=", oldCode);
		}
		boolean hasRow = sqlParser.hasRow();
		if (hasRow) {
			throw new HtException(String.format("列名[%s]已存在~", newCode));
		}
	}
	
	/**
	 * 防止操作系统表
	 */
	private void checkSystemTable(String tableCode) {
		if (SYS_TABLES.contains(tableCode)) {
			throw new HtException(String.format("不允许操作系统表：[%s]", tableCode));
		}
	}
	
	/**
	 * 防止操作系统字段
	 */
	private void checkSystemColumn(Entity entity) {
		// 防止操作系统表
		checkSystemTable(entity.getStr("table_code"));
		
		// 防止操作其它表的系统字段
		String columnCode = entity.getStr("code");
		if (SYS_COLUMNS.contains(columnCode)) {
			throw new HtException(String.format("不允许操作系统字段：[%s]", columnCode));
		}
	}
	
	/**
	 * 检查json参数是否有指定的字段
	 * @param entity json参数对象
	 * @param fields 指定json参数中必须包含的字段列表
	 */
	private void checkField(Entity entity, String... fields) {
		for (String field : fields) {
			if (!entity.containsKey(field)) {
				throw new HtException(String.format("json参数需要%s字段~", field));
			}
			if (StrUtil.isBlankIfStr(entity.get(field))) {
				throw new HtException(String.format("%s字段不能为空~", field));
			}
		}
	}

	/**
	 * 去除空格
	 * @param entity
	 */
	private void trim(Entity entity) {
		List<String> fields = Arrays.asList("table_code", "code", "alias");
		fields.stream().forEach(f -> {
			if (entity.containsKey(f)) {
				String value = entity.getStr(f);
				entity.set(f, StrUtil.cleanBlank(value));
			}
		});
	}
}
